package com.applcn.user.service;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.applcn.core.base.PageVO;
import com.applcn.core.base.Result;
import com.applcn.user.constant.AuthRangeEnum;
import com.applcn.user.dto.merchant.*;
import com.applcn.user.repo.entity.MenuEntity;
import com.applcn.user.repo.entity.RoleEntity;
import com.applcn.user.repo.entity.RoleMenuEntity;
import com.applcn.user.repo.entity.UserRoleEntity;
import com.applcn.user.repo.repository.MenuRepository;
import com.applcn.user.repo.repository.RoleMenuRepository;
import com.applcn.user.repo.repository.RoleRepository;
import com.applcn.user.repo.repository.UserRoleRepository;
import com.applcn.user.threadlocal.UserRequestContextHolder;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * com.applcn.user.service
 * Description:
 *
 * @author: 大妖怪
 * @date: 2020-03-17 02:03
 */
@Service
@RequiredArgsConstructor
public class RoleService {

    private final RoleRepository roleRepository;
    private final CommonService commonService;
    private final RoleMenuRepository roleMenuRepository;
    private final MenuRepository menuRepository;
    private final UserRoleRepository userRoleRepository;

    /**
     * 分页查询角色列表
     *
     * @param dto
     * @return
     */
    public Result pageRole(PageRoleReqDTO dto) {
        commonService.checkShop(dto.getShopId());
        IPage<RoleEntity> roleIPage = roleRepository.pageRole(dto);
        List<PageRoleRespDTO> pageRoleRespDTOList = new ArrayList<>();
        for (RoleEntity roleEntity : roleIPage.getRecords()) {
            PageRoleRespDTO pageRoleRespDTO = new PageRoleRespDTO();
            pageRoleRespDTO.setId(roleEntity.getId());
            pageRoleRespDTO.setName(roleEntity.getName());
            pageRoleRespDTO.setIntroduction(roleEntity.getIntroduction());
            pageRoleRespDTOList.add(pageRoleRespDTO);
        }
        PageVO<PageRoleRespDTO> pageVO = new PageVO();
        pageVO.setRecords(pageRoleRespDTOList);
        pageVO.assemblePage(roleIPage);
        return Result.ok().putData(pageVO);
    }

    /**
     * 创建/修改角色
     *
     * @param dto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Result saveOrUpdateRole(SaveOrUpdateRoleReqDTO dto) {
        commonService.checkShop(dto.getShopId());
        RoleEntity roleEntity;
        if (dto.getId() != null) {
            roleEntity = roleRepository.getById(dto.getId());
            Assert.notNull(roleEntity, "角色不存在！");
            Assert.isTrue(dto.getShopId().equals(roleEntity.getShopId()), "没有操作权限！");
        } else {
            roleEntity = new RoleEntity();
            Assert.notBlank(dto.getName());
            AuthRangeEnum authRange = AuthRangeEnum.getAuthRange(dto.getAuthRange());
            Assert.notNull(authRange, "权限范围不能为空！");
            roleEntity.setAuthRange(authRange);
            roleEntity.setShopId(dto.getShopId());
        }
        if (StrUtil.isNotBlank(dto.getName())) {
            roleEntity.setName(dto.getName());
        }
        if (StrUtil.isNotBlank(dto.getIntroduction())) {
            roleEntity.setIntroduction(dto.getIntroduction());
        }

        roleRepository.saveOrUpdate(roleEntity);

        Set<RoleMenuEntity> roleMenuEntitySet = roleMenuRepository.getRoleMenuListByRoleIdList(Lists.newArrayList(roleEntity.getId()));
        for (RoleMenuEntity roleMenuEntity : roleMenuEntitySet) {
            if (!dto.getMenuIds().contains(roleMenuEntity.getMenuId())) {
                roleMenuRepository.removeById(roleMenuEntity);
                dto.getMenuIds().remove(roleMenuEntity.getMenuId());
            }
        }

        List<RoleMenuEntity> roleMenuEntityList = new ArrayList<>();
        for (Long menuId : dto.getMenuIds()) {
            RoleMenuEntity roleMenuEntity = new RoleMenuEntity();
            roleMenuEntity.setMenuId(menuId);
            roleMenuEntity.setRoleId(roleEntity.getId());
            roleMenuEntityList.add(roleMenuEntity);
        }
        roleMenuRepository.saveBatch(roleMenuEntityList);
        return Result.ok();
    }

    /**
     * 批量删除角色
     *
     * @param dto
     * @return
     */
    public Result deleteRoleByIds(DeleteRoleByIdsReqDTO dto) {
        commonService.checkShop(dto.getShopId());
        roleRepository.deleteBatch(dto.getIds(), dto.getShopId());
        return Result.ok();
    }

    /**
     * 查询角色详情
     *
     * @param dto
     * @return
     */
    public Result roleDetail(RoleDetailReqDTO dto) {
        commonService.checkShop(dto.getShopId());
        RoleEntity roleEntity = roleRepository.getById(dto.getId());
        Assert.notNull(roleEntity, "角色不存在！");
        Set<RoleMenuEntity> roleMenuEntitySet = roleMenuRepository.getRoleMenuListByRoleIdList(Lists.newArrayList(roleEntity.getId()));
        List<MenuEntity> menuEntityList = menuRepository.listByShopId(dto.getShopId());
        List<RoleDetailRespDTO.MenuList> menuList = assembleMenuList(0L, menuEntityList, roleMenuEntitySet);
        RoleDetailRespDTO roleDetailRespDTO = new RoleDetailRespDTO();
        roleDetailRespDTO.setId(roleEntity.getId());
        roleDetailRespDTO.setIntroduction(roleEntity.getIntroduction());
        roleDetailRespDTO.setName(roleEntity.getName());
        roleDetailRespDTO.setMenuListList(menuList);
        return Result.ok().putData(roleDetailRespDTO);
    }

    /**
     * 递归装配菜单列表
     *
     * @param parentId
     * @param menuEntityList
     * @return
     */
    private List<RoleDetailRespDTO.MenuList> assembleMenuList(Long parentId, List<MenuEntity> menuEntityList,
                                                              Set<RoleMenuEntity> roleMenuEntitySet) {
        List<MenuEntity> parentList = menuEntityList.stream().filter(e -> e.getParentId().equals(parentId)).collect(Collectors.toList());
        Collections.sort(parentList);
        menuEntityList.removeAll(parentList);
        List<RoleDetailRespDTO.MenuList> menuListList = new ArrayList<>();
        for (MenuEntity menuEntity : parentList) {
            RoleDetailRespDTO.MenuList menuList = new RoleDetailRespDTO.MenuList();
            menuList.setId(menuEntity.getId());
            menuList.setName(menuEntity.getName());
            menuList.setChoose(roleMenuEntitySet.contains(menuEntity));
            menuList.setChildren(assembleMenuList(menuEntity.getId(), menuEntityList, roleMenuEntitySet));
            menuEntityList.add(menuEntity);
        }
        return menuListList;
    }

    /**
     * 创建用户角色
     * <p>1.创建角色</p>
     * <p>2.关联用户</p>
     *
     * @param name
     * @param shopId
     * @param userId
     * @param authRange
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public RoleEntity createUserRole(String name, Long shopId, Long userId, AuthRangeEnum authRange) {
        RoleEntity roleEntity = new RoleEntity();
        roleEntity.setName(name);
        roleEntity.setShopId(shopId);
        roleEntity.setAuthRange(authRange);
        roleRepository.save(roleEntity);

        UserRoleEntity userRoleEntity = new UserRoleEntity();
        userRoleEntity.setRoleId(roleEntity.getId());
        userRoleEntity.setUserId(userId);
        userRoleRepository.save(userRoleEntity);
        return roleEntity;
    }

    /**
     * 查询角色列表
     *
     * @param authRangeEnum
     * @return
     */
    public Result shopRoleList(AuthRangeEnum authRangeEnum) {
        Long shopId = UserRequestContextHolder.getShopId();
        Assert.notNull(shopId, "请选择店铺！");
        List<RoleEntity> roleEntityList = roleRepository.getShopRoleList(authRangeEnum, shopId, null);
        List<ShopRoleListRespDTO> shopRoleListRespDTOList = new ArrayList<>();
        for (RoleEntity roleEntity : roleEntityList) {
            ShopRoleListRespDTO dto = new ShopRoleListRespDTO();
            dto.setId(roleEntity.getId());
            dto.setName(roleEntity.getName());
            dto.setIntroduction(roleEntity.getIntroduction());
            shopRoleListRespDTOList.add(dto);
        }

        return Result.ok().putData(shopRoleListRespDTOList);
    }
}
